版权声明:本文为博主原创文章,转载请注明出处:http://blog.jerkybible.com/2015/05/19/Spring MVC 之 contextcomponent-scan/
一、ContextLoader
通过阅读SpringMVC的源码可知,SpringMVC的初始化在ContextLoaderListener类中的contextInitialized方法,跟一下代码发现实际上是在ContextLoader中的initWebApplicationContext方法中进行初始化。初始化的代码如下,首先会创建一个WebApplicationContext对象,然后加载父类,接着使用configureAndRefreshWebApplicationContext初始化这个WebApplicactionContext对象,是在configureAndRefreshWebApplicationContext方法中进行了配置文件的加载和组件的扫描。
展开createWebApplicationContext方法。该方法初始化根WebApplicationContext,这个WebApplicationContext可以使默认的context也尅进行定制。
展开determineContextClass方法。这个方法比较简单,首先查看有没有配置CONTEXT_CLASS_PARAM这个属性,有的话使用这个属性配置的类进行加载,否则使用默认的类。
再看configureAndRefreshWebApplicationContext方法。configureAndRefreshWebApplicationContext最后的refresh方法已经在前面的Spring 使用简单Demo进行源码调试(一)系列文章里进行了简单的分析调试。
二、 <context:component-scan/>
我们在SpringMVC开发项目中,有的用注解和XML配置Bean,这两种都各有自己的优势,数据源配置比较经常用XML配置,控制层依赖的service比较经常用注解等(在部署时比较不会改变的),我们经常比较常用的注解有@Component是通用标注,@Controller标注web控制器,@Service标注Servicec层的服务,@Respository标注DAO层的数据访问。SpringMVC启动时怎么被自动扫描然后解析并注册到Bean工厂中去(放到DefaultListableBeanFactory中的Map
@Controller标注web控制器,@Service标注Service层的服务,@Respository标注DAO层的数据访问。@Component是通用标注,只是定义为一个类为Bean,SpringMVC会把所有添加@Component注解的类作为使用自动扫描注入配置路径下的备选对象。@Controller、@Service\@Respository只是更加的细化,都是被@Component标注,所以我们比较不推荐使用@Component。源代码如下:
都是有标示@Component
我们在配置文件中,标示配置需要扫描哪些包下,也可以配置对某个包下不扫描,代码如下:
说明:
SpringMVC先读取配置文件,然后根据context:component-scan中属性base-package去扫描指定包下的class和jar文件,把标示@Controller标注web控制器,@Service标注Servicec层的服务,@Respository标注DAO层的数据访问等注解的都获取,并注册为Bean类放到Bean工厂。
三、ComponentScanBeanDefinitionParser
通过阅读源码,知道接口BeanDefinitionParser可以实现将自定义的标签转化为 BeanDefinition类。而
展开ComponentScanBeanDefinitionParser中的parse方法。可以看到主要完成以下工作。
(1)获取context:component-scan 配置的属性base-package的值,然后放到数组。
(2)创建扫描对应包下的class和jar文件的对象ClassPathBeanDefinitionScanner ,由这个类来实现扫描包下的class和jar文件并把注解的Bean包装成BeanDefinition。
(3)BeanDefinition注册到Bean工厂。
再展开configureScanner方法。首先通过findCandidateComponents获取候选bean definition。然后注册到registry中。
四、筛选类
展开findCandidateComponents方法。首先获取路径下的资源Resource,然后判断资源是否可读,并且获取可读资源的MetadataReader对象,然后再调用isCandidateComponent(MetadataReader)判段是否是候选组件,如果是,则生成该metadataReader的ScannedGenericBeanDefinition对象。最后判断ScannedGenericBeanDefinition是否为候选的,如果是则添加到工厂中。
展开isCandidateComponent方法。通过变量excludeFilters, includeFilters去匹配传递进来的MetadataReader,如果与excludeFilter匹配成功返回false, 与includeFilter匹配成功返回true。
五、注册bean factory
在上面doScan方法中有
这样一行代码将beanDefinition注册到registry中,这是一个BeanDefinitionRegistry,下面是它的接口定义及继承结构:
|
|
我们可以看到接口中定义了诸多beandefinition的注册,删除,获取等方法,并且Spring为我们提供了三个内部实现,那么运行时,和之前文章的分析一样,使用了DefaultListableBeanFactory。
展开registerBeanDefinition方法。可以看出,所有的beanDefinition都由实例变量beanDefinitionMap来保存管理,他是一个ConcurrentHashMap,beanName作为键,beanDefinition对象作为值。